package tech.mhuang.ext.interchan.autoconfiguration.datasecure.advice;

import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;
import tech.mhuang.core.io.IOUtil;
import tech.mhuang.core.util.ObjectUtil;
import tech.mhuang.core.util.StringUtil;
import tech.mhuang.ext.interchan.autoconfiguration.datasecure.DataSecureInfo;
import tech.mhuang.ext.interchan.autoconfiguration.datasecure.DataSecureProperties;
import tech.mhuang.ext.interchan.autoconfiguration.datasecure.annation.DataSecureField;
import tech.mhuang.ext.interchan.autoconfiguration.datasecure.annation.DecryptMapping;
import tech.mhuang.ext.interchan.autoconfiguration.datasecure.consts.DecryptType;
import tech.mhuang.ext.interchan.core.exception.BusinessException;
import tech.mhuang.ext.interchan.protocol.Result;
import tech.mhuang.ext.spring.util.DataUtil;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Type;


/**
 * 请求数据解密
 *
 * @author mhuang
 * @since 1.6.2
 */
@ControllerAdvice
public class DataSecureRequestBodyAdvice extends RequestBodyAdviceAdapter {

    private DataSecureProperties secretProperties;

    public DataSecureRequestBodyAdvice(DataSecureProperties properties) {
        this.secretProperties = properties;
    }

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return methodParameter.getMethod().isAnnotationPresent(DecryptMapping.class) ||
                methodParameter.getContainingClass().isAnnotationPresent(DecryptMapping.class);
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        DecryptMapping decryptMapping = methodParameter.getMethod().getAnnotation(DecryptMapping.class);
        if (ObjectUtil.isEmpty(decryptMapping)) {
            decryptMapping = methodParameter.getContainingClass().getAnnotation(DecryptMapping.class);
        }

        //获取配置项
        DataSecureInfo dataSecureInfo;
        String configKey = decryptMapping.value();
        if (StringUtil.isEmpty(configKey)) {
            dataSecureInfo = secretProperties;
        } else if (secretProperties.getDataSecurePropertiesMap().containsKey(configKey)) {
            dataSecureInfo = secretProperties.getDataSecurePropertiesMap().get(configKey);
        } else {
            throw new BusinessException(Result.SYS_FAILD, String.format("找不到配置项:%s", configKey));
        }

        //应答数据
        String httpBody;
        //整体解密
        if (decryptMapping.type() == DecryptType.ALL) {
            httpBody = decryptBody(httpInputMessage, dataSecureInfo);
        } else {
            //字段解密
            Field[] fields = methodParameter.getParameter().getType().getDeclaredFields();
            Object entity = JSON.parseObject(toString(httpInputMessage, dataSecureInfo), type);
            for (Field field : fields) {
                //需要处理的字段
                if (field.isAnnotationPresent(DataSecureField.class)) {
                    DataSecureField dataSecureField = field.getAnnotation(DataSecureField.class);
                    try {
                        Object proceeFieldValue = DataUtil.getValueByModelKey(entity, field.getName(), field.getType());
                        if (dataSecureField.decode() && ObjectUtil.isNotEmpty(proceeFieldValue)) {
                            DataUtil.setValueByModel(entity, field.getName(), dataSecureField.Clazz().newInstance().decrypt(
                                    proceeFieldValue instanceof String ? (String) proceeFieldValue : JSON.toJSONString(proceeFieldValue),
                                    dataSecureInfo.getPrivateKey()
                            ));
                        }
                    } catch (Exception e) {
                        throw new BusinessException(Result.SYS_FAILD, "处理字段异常", e);
                    }
                }
            }
            httpBody = JSON.toJSONString(entity);
        }
        return new DataSecureHttpMessage(new ByteArrayInputStream(httpBody.getBytes()), httpInputMessage.getHeaders());
    }

    private String decryptBody(HttpInputMessage httpInputMessage, DataSecureInfo dataSecureInfo) throws IOException {
        String encryptBody = toString(httpInputMessage, dataSecureInfo);
        try {
            return dataSecureInfo.getEncryptDataInterface().newInstance().decrypt(encryptBody, dataSecureInfo.getPrivateKey());
        } catch (Exception e) {
            throw new BusinessException(Result.SYS_FAILD, "数据解密异常", e);
        }
    }

    private String toString(HttpInputMessage httpInputMessage, DataSecureInfo dataSecureInfo) throws IOException {
        InputStream encryptStream = httpInputMessage.getBody();
        try {
            return IOUtil.toString(encryptStream, dataSecureInfo.getEncryptDataInterface().newInstance().getDecryptCoding().toString());
        } catch (Exception e) {
            throw new BusinessException(Result.SYS_FAILD, "数据解密异常", e);
        }
    }

    @Data
    @AllArgsConstructor
    class DataSecureHttpMessage implements HttpInputMessage {
        private InputStream body;
        private HttpHeaders httpHeaders;

        @Override
        public InputStream getBody() {
            return this.body;
        }

        @Override
        public HttpHeaders getHeaders() {
            return this.httpHeaders;
        }
    }
}
